#include <stdlib.h>
#include <stdio.h>

#include "iterlistKM.h"

#ifdef _DEBUG
#define ASSERT(x)   !(x) ? PrintErrAbort(#x,__FILE__,__LINE__) : 0
#else
#define ASSERT(x)
#endif /*#ifdef_DEBUG*/

#define NO_MANS_LAND    0xdeadbeef

/*-------------------------------------------------------------------*/
/*                            Structs                                */
/*-------------------------------------------------------------------*/
struct Node_t
{
    void*   data;
    struct Node_t* next;
    struct Node_t* prev;
};

typedef struct Node_t Node;

struct List_t
{
    Node head;
    Node tail;
};

/*-------------------------------------------------------------------*/
/*                            Functions                              */
/*-------------------------------------------------------------------*/

#ifdef _DEBUG
int PrintErrAbort(char* _expression,char* _file,int _lineNum)
{
    puts("The program was Aborted!!");
    printf("The abortion was caused by:\n");
    printf("'%s'\t expression\nIn file: %s\nIn line: %d\n",_expression,_file,_lineNum);
    exit(1);
	return 0;
}
#endif /*#ifdef __DEBUG*/

/*-------------------------------------------------------------------*/

List* ListNew(void)
{
    List* newList = 0;

    newList = malloc(sizeof(newList[0]));
    if (!newList)
    {
        return 0;
    }

    newList->head.data = (void*)NO_MANS_LAND;   /*forbidden address no one can reach it*/
    newList->tail.data = (void*)NO_MANS_LAND;
    newList->tail.next = (Node*)NO_MANS_LAND;
    newList->head.prev = (Node*)NO_MANS_LAND;
    newList->head.next = &newList->tail;
    newList->tail.prev = &newList->head;
    return newList;
}

/*-------------------------------------------------------------------*/

ListIter ListBegin(List* _list)
{
    ASSERT(_list);

    return (ListIter)_list->head.next;
}

/*-------------------------------------------------------------------*/

ListIter ListEnd(List* _list)
{
    ASSERT(_list);

    return (ListIter)&_list->tail;
}

/*-------------------------------------------------------------------*/

void* ListGetData(ListIter _iter)
{
    ASSERT(_iter);

    return ((Node*)_iter)->data;
}

/*-------------------------------------------------------------------*/

ListIter ListNext(ListIter _iter)
{
    ASSERT(((Node*)_iter)->next);

    return (ListIter)((Node*)_iter)->next;
}

/*-------------------------------------------------------------------*/

ListIter ListPrev(ListIter _iter)
{
    ASSERT(((Node*)_iter)->prev);

    return (ListIter)((Node*)_iter)->prev;
}

/*-------------------------------------------------------------------*/

ListIter ListInsert(List* _list, ListIter _iter, void* _data)
{
    Node*   newNode = 0;
    ASSERT(_iter);
    ASSERT(((Node*)_iter)->prev);

    newNode = (Node*)malloc(sizeof(newNode[0]));
    if (!newNode)
    {
        return ListEnd(_list);
    }

    newNode->data        = _data;
    newNode->next        = (Node*)_iter;
    newNode->prev        = ((Node*)_iter)->prev;
    ((Node*)_iter)->prev->next = newNode;
    ((Node*)_iter)->prev = newNode;

    return (ListIter)newNode;
}

/*-------------------------------------------------------------------*/

int ListIsEmpty(List* _list)
{
    return ListBegin(_list) == ListEnd(_list);
}

/*-------------------------------------------------------------------*/

size_t ListCount(List* _list)
{
    ListIter it      = ListBegin(_list);
    int      counter = 0;

    while (it != ListEnd(_list))
    {
        ++counter;
        it = ListNext(it);
    }

    return counter;
}

/*-------------------------------------------------------------------*/

ListIter ListErase(ListIter _iter)
{
    ASSERT(((Node*)_iter));
    ASSERT(((Node*)_iter)->prev);
    ASSERT(((Node*)_iter)->next);

    ((Node*)_iter)->next->prev = ((Node*)_iter)->prev;

    _iter = ListNext(_iter);
    ((Node*)_iter)->prev->next->data = (void*)NO_MANS_LAND; /*demolish data of object in case someone try to reach it*/
    free(((Node*)_iter)->prev->next);
    ((Node*)_iter)->prev->next= (Node*)_iter;
    return _iter;
}

/*-------------------------------------------------------------------*/

void ListDel(List* _list)
{
    ASSERT(_list);

    while (!ListIsEmpty(_list))
    {
        ListErase(ListBegin(_list));
    }
    free(_list);
}

/*-------------------------------------------------------------------*/
